#include "st_waiter_thread.h"
#include "st_sock_holder.h"
#include <iostream>
using std::cout;
using std::endl;

STWaiterThread::STWaiterThread()
{
    m_msgSock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    if (m_msgSock != -1)
    {
        // we needn't bind UDP sockets?
        socklen_t size = sizeof(m_msgSockAddr);
		memset(&m_msgSockAddr, 0, sizeof(m_msgSockAddr));
		m_msgSockAddr.sin_family = AF_INET;
#ifdef _WIN32
        m_msgSockAddr.sin_addr.S_un.S_un_b.s_b1 = 127;
		m_msgSockAddr.sin_addr.S_un.S_un_b.s_b4 = 1;
#endif
        bind(m_msgSock, (sockaddr*)&m_msgSockAddr, size);
        getsockname(m_msgSock, (sockaddr*)&m_msgSockAddr, &size);

        m_maxFd = m_msgSock;
        FD_ZERO(&m_fdset);
        FD_SET((unsigned int)m_msgSock, &m_fdset);
    }
}

STWaiterThread::~STWaiterThread()
{
    stop();
    if (m_msgSock != -1)
    {
        closesocket(m_msgSock);
        m_msgSock = -1;
    }
}

void STWaiterThread::run()
{
    cout << "STWaiterThread::run thread started" << endl;
    while (true)
    {
        fd_set fds;
        lock();
        fds = m_fdset;
        unlock();
        int rc = select(m_maxFd + 1, &fds, 0, 0, 0);
        //cout << "STWaiterThread::run: select returns " << rc << endl;
        if (rc < 0)
            break;
        else if (rc > 0)
        {
            if (FD_ISSET(m_msgSock, &fds))
            {
                if (handleMsg() == false)
                    break;
                else
                    continue;
            }
            lock();
            STSockHolder* holderWithData = 0;
            for (list<STSockHolder*>::iterator iter = m_sockHolders.begin();
                iter != m_sockHolders.end();
                ++iter)
            {
                STSockHolder* holder = *iter;
                if (FD_ISSET((unsigned int)holder->getSock(), &fds))
                {
                    holderWithData = holder;
                    break;
                }
            }
            unlock();
            if (holderWithData != 0)
            {
                char buffer[200];
                // should be non block? We need to use a nonblock option?
                int size = recv(holderWithData->getSock(), buffer, sizeof(buffer), 0);
                if (size < 0 || holderWithData->pushBytes(buffer, size) == false)
                {
                    this->removeSockHolder(holderWithData);
                    delete holderWithData;
                }
                else if ((size == 0) && (holderWithData->pushEnd() == false))
                {
                    this->removeSockHolder(holderWithData);
                    delete holderWithData;
                }
            }
        }
    }
    cout << "STWaiterThread::run: return here" << endl;
}

void STWaiterThread::stop()
{
    // send a packet of size 0
    if (0 == sendto(m_msgSock, "", 0, 0, (sockaddr*)&m_msgSockAddr, sizeof(m_msgSockAddr)))
        join();
    else
        throw "Failed to stop thread";
}

bool STWaiterThread::addSockHolder(STSockHolder* sockHolder)
{
    bool ret = false;
    lock();
    if (m_sockHolders.size() < MAX_NUM_SOCKS)
    {
        m_sockHolders.push_back(sockHolder);
        rebuildFdSet();
        ret = true;
    }
    unlock();
    return ret;
}

bool STWaiterThread::removeSockHolder(STSockHolder* sockHolder)
{
    bool ret = false;
    lock();
    ret = removeSockHolder_unlocked(sockHolder);
    unlock();
    return ret;
}

bool STWaiterThread::removeSockHolder_unlocked(STSockHolder* sockHolder)
{
    m_sockHolders.remove(sockHolder);
    rebuildFdSet();
    return true;
}

void STWaiterThread::rebuildFdSet()
{
    FD_ZERO(&m_fdset);
    FD_SET((unsigned int)m_msgSock, &m_fdset);
    list<STSockHolder*>::iterator iter = m_sockHolders.begin();
    m_maxFd = m_msgSock;
    while (iter != m_sockHolders.end())
    {
        STSockHolder* holder = *iter;
        int sock = holder->getSock();
        if (sock > m_maxFd)
            m_maxFd = sock;
        FD_SET((unsigned int)sock, &m_fdset);
        ++iter;
    }

    // ask the sleeping thread to wake up
    sendMsg("wakeup", 7);
}

/**
 * Handle command. Generally control command.
 * @return true for OK; false for failure.
 */
bool STWaiterThread::handleMsg()
{
    char buffer[1000];
    sockaddr_in addr;
    socklen_t addrSize = sizeof(addr);
    int size = recvfrom(m_msgSock, buffer, sizeof(buffer), 0, (sockaddr*)&addr, &addrSize);
    if (size <= 0)
        return false;
    else
    {
        // TODO: process the message
        // cout << "STWaiterThread::handleMsg: handling msg " << buffer << endl;
    }
    return true;
}

void STWaiterThread::sendMsg(const void* msg, unsigned int msgLen)
{
    int rc = sendto(m_msgSock, (char*)msg, msgLen, 0, (sockaddr*)&m_msgSockAddr, sizeof(m_msgSockAddr));
	if (rc < 0)
	{
		throw ("STWaiterThread::sendMsg: error");
	}
}
